
// Copyright (C) 2012 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Xml;

using Derm.Render;

namespace Derm.Xml.Collada
{
	/// <summary>
	/// Class grouping common input semantics.
	/// </summary>
	public static class ColladaInputSemantic
	{
		/// <summary>
		/// Color coordinate vector. Color inputs are RGB (float3).
		/// </summary>
		public const string Color = "COLOR";

		/// <summary>
		/// Normal vector.
		/// </summary>
		public const string Normal = "NORMAL";

		/// <summary>
		/// Texture coordinate vector.
		/// </summary>
		public const string TexCoord = "TEXCOORD";

		/// <summary>
		/// Mesh vertex.
		/// </summary>
		public const string Vertex = "VERTEX";

		/// <summary>
		/// Convert a COLLADA input semantic into a Derm shader attribute semantic.
		/// </summary>
		/// <param name="semantic"></param>
		/// <returns></returns>
		public static string ConvertSemantic(string semantic)
		{
			switch (semantic) {
				case Color:
					return (VertexArraySemantic.Color);
				case Normal:
					return (VertexArraySemantic.Normal);
				case TexCoord:
					return (VertexArraySemantic.TexCoord);
				case Vertex:
					return (VertexArraySemantic.Position);
				default:
					return (semantic);
			}
		}
	}

	/// <summary>
	/// The InputGlobal type is used to represent inputs that can reference external resources.
	/// </summary>
	public class ColladaInputGlobal : ColladaXmlSerializable
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaInputGlobal.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaInputGlobal.
		/// </param>
		public ColladaInputGlobal(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Input Global Information

		/// <summary>
		/// The semantic attribute is the user-defined meaning of the input connection. Required attribute.
		/// </summary>
		public string Semantic;

		/// <summary>
		/// The source attribute indicates the location of the data source. Required attribute.
		/// </summary>
		public string Source;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the attribute of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading XML element attributes.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method is called only in the case there are effectively attributes on XML element. Don't expect that this
		/// method will be called even when the attributes count is zero. This is very important, since the design doesn't
		/// allow object initialization using this method.
		/// </para>
		/// </remarks>
		public override void ReadAttributes(XmlReader xmlReader)
		{
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			string attribute;

			if ((attribute = xmlReader[ColladaAttributeSemantic]) == null)
				throw new InvalidOperationException("required attribute " + ColladaAttributeSemantic);
			Semantic = attribute;

			if ((attribute = xmlReader[ColladaAttributeSource]) == null)
				throw new InvalidOperationException("required attribute " + ColladaAttributeSource);
			Source = attribute;
		}

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeSemantic = "semantic";

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeSource = "source";

		#endregion
	}

	/// <summary>
	/// The InputLocal type is used to represent inputs that can only reference resources declared in the same document.
	/// </summary>
	public class ColladaInputLocal : ColladaXmlSerializable
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaInputLocal.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaInputLocal.
		/// </param>
		public ColladaInputLocal(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Input Local Information

		/// <summary>
		/// The semantic attribute is the user-defined meaning of the input connection. Required attribute.
		/// </summary>
		public string Semantic;

		/// <summary>
		/// The source attribute indicates the location of the data source. Required attribute.
		/// </summary>
		public string Source;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the attribute of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading XML element attributes.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method is called only in the case there are effectively attributes on XML element. Don't expect that this
		/// method will be called even when the attributes count is zero. This is very important, since the design doesn't
		/// allow object initialization using this method.
		/// </para>
		/// </remarks>
		public override void ReadAttributes(XmlReader xmlReader)
		{
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			string attribute;

			if ((attribute = xmlReader[ColladaAttributeSemantic]) == null)
				throw new InvalidOperationException("required attribute " + ColladaAttributeSemantic);
			Semantic = attribute;

			if ((attribute = xmlReader[ColladaAttributeSource]) == null)
				throw new InvalidOperationException("required attribute " + ColladaAttributeSource);
			Source = attribute;
		}

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeSemantic = "semantic";

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeSource = "source";

		#endregion
	}

	/// <summary>
	/// The InputLocalOffset type is used to represent indexed inputs that can only reference resources declared in the same document.
	/// </summary>
	public class ColladaInputLocalOffset : ColladaInputLocal
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaInputLocalOffset.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaInputLocalOffset.
		/// </param>
		public ColladaInputLocalOffset(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Input Local Offset Information

		/// <summary>
		/// The offset attribute represents the offset into the list of indices.  If two input elements share 
		/// the same offset, they will be indexed the same.  This works as a simple form of compression for the 
		/// list of indices as well as defining the order the inputs should be used in.  Required attribute.
		/// </summary>
		public uint Offset;

		/// <summary>
		/// The set attribute indicates which inputs should be grouped together as a single set. This is helpful 
		/// when multiple inputs share the same semantics. 
		/// </summary>
		public uint Set = UInt32.MaxValue;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the attribute of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading XML element attributes.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method is called only in the case there are effectively attributes on XML element. Don't expect that this
		/// method will be called even when the attributes count is zero. This is very important, since the design doesn't
		/// allow object initialization using this method.
		/// </para>
		/// </remarks>
		public override void ReadAttributes(XmlReader xmlReader)
		{
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			string attribute;

			// Base implementation
			base.ReadAttributes(xmlReader);

			if ((attribute = xmlReader[ColladaAttributeOffset]) == null)
				throw new InvalidOperationException("required attribute " + ColladaAttributeOffset);
			Offset = UInt32.Parse(attribute);

			if ((attribute = xmlReader[ColladaAttributeSet]) != null)
				Set = UInt32.Parse(attribute);
		}

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeOffset = "offset";

		/// <summary>
		/// Collada 'geometry' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeSet = "set";

		#endregion
	}
}
